#include "pfnode.h"
#include <boost/asio.hpp>
#include "trace.h"
#include <stdio.h>
#include <QDebug>
//#include <QFile>
//#include <QTextStream>
#include <fstream>

PFNode::PFNode(QObject *parent) :
    QObject(parent)
{
}

PFNode::PFNode(std::string name, int totalThumbImg, QObject *parent) :
        QObject(parent)
{
    m_IsStreaming = false;
    m_IsWatching = false;
    m_IsCapturing = false;
    m_WatchingCam = 0;
    setNodeName(name);
    for(int i=0;i<totalThumbImg;i++)
    {
        GraphicsIplImgObject *thm = new GraphicsIplImgObject(PFCMU::WIDTH/4,PFCMU::HEIGHT/4);
        thm->setCurrentCam(i);
        thm->setThumb();
        m_ThumbImg.push_back(thm);
        connect(thm,SIGNAL(objectDoubleClicked(int)),SLOT(setToWatching(int)));
    }
    m_LiveImg = new GraphicsIplImgObject();
//    QFile newFile("para.txt");
//    newFile.open(stderr, QIODevice::ReadOnly);
//    QTextStream tsr(&newFile);
//    double shutter;
//    tsr >> shutter;
//    double gain;
//    tsr >> gain;
//    qDebug() << "shutter:" << shutter << " gain:" << gain;
    std::ifstream file("/home/demo/argus-cmu-old/tools/live-viewer/src/para.txt");
    if (file.good()) {
       double shutter, gain;
       file >> shutter;
       file >> gain;
       m_Shutter = shutter; //PFCMU::DEFAULTSHUTTER;
       m_Gain = gain; //PFCMU::DEFAULTGAIN;
   } else {
       qDebug() << "Cannot read para.txt";
       m_Shutter = PFCMU::DEFAULTSHUTTER;
       m_Gain = PFCMU::DEFAULTGAIN;
   }
}

void PFNode::startConnection()
{
    m_IsInit = true;
    boost::thread netTrd(boost::bind(&PFNode::m_InitLiveConnect, this,
                                     m_NodeName, INITPORT));
}

void PFNode::m_InitLiveConnect(std::string nodeName, std::string nodePort)
{
    try
    {
        //
        m_IsStreaming = false;
        using namespace boost::asio::ip;
        //tcp::iostream nodeStream(nodeName, nodePort);
        tcp::iostream initStream(nodeName, nodePort);
        TRACE(1, "To %s: Init connection start\n", m_NodeName.c_str());
        std::string initCmd;
        std::getline(initStream, initCmd);
        if(initCmd=="REDY")
        {
            TRACE(1, "Fm %s: %s message received\n", m_NodeName.c_str(), initCmd.c_str());
        }
        initStream << "ENPF\n"
                << PFCMU::DEFAULTFPS << "\n"
                << m_Shutter << "\n"
                << m_Gain << "\n"
                << std::flush;
        std::string initLine;
        std::getline(initStream, initLine);
        std::istringstream initIss(initLine);
        int recvFps;
        initIss >> initCmd >> recvFps;
        if(initCmd=="PFRY")
        {
            TRACE(1, "Fm %s: ProFUSION is running at %d fps\n", m_NodeName.c_str(), recvFps);
            TRACE(1, "To %s: Now going into Live\n", m_NodeName.c_str());
            tcp::iostream lvInitStream(nodeName, CMDPORT);
            lvInitStream << "ENLV" << std::endl;
            TRACE(1, "To %s: Sending Enter Live command\n", m_NodeName.c_str());
            std::string initLiveCmd;
            std::string initLiveLine;
            int livePort;
            std::getline(lvInitStream, initLiveLine);
            std::istringstream initLiveIss(initLiveLine);
            initLiveIss >> initLiveCmd >> livePort;
            if(initLiveCmd=="LVRY")
            {
                TRACE(1, "Fm %s: Live view server is ready on port %d\n", m_NodeName.c_str(), livePort);
                m_IsStreaming = true;
                //start the live streaming in new thread
                boost::thread netTrd(boost::bind(&PFNode::m_LiveStreamingConnect, this,
                                                 m_NodeName, LIVEPORT));
            }
        }
    }
    catch(std::exception &e)
    {
        //
    }
}

void PFNode::m_LiveStreamingConnect(std::string nodeName, std::string nodePort)
{
    IplImage *blackBuf = NULL;
    IplImage *thumbBuf = NULL;
    try
    {
        using namespace boost::asio::ip;
        //tcp::iostream nodeStream(nodeName, nodePort);
        tcp::iostream liveStream(nodeName, nodePort);
        std::string liveCmd;
        liveStream << "INIT" <<std::endl;
        TRACE(1, "To %s: Live INIT command sended\n", m_NodeName.c_str());
        std::getline(liveStream,liveCmd);
        if(liveCmd=="REDY")
        {
            TRACE(1, "Fm %s: START live!!!\n", m_NodeName.c_str());
            while(m_IsStreaming)
            {
                char liveStr[1024];
                if(m_IsWatching)
                {
                    //THMW
                    if(m_WatchingCam<0 && m_WatchingCam>23)
                    {
                        m_WatchingCam=0;
                    }
                    snprintf(liveStr,sizeof(liveStr),"THMW %d",m_WatchingCam);
                    liveStream << liveStr << std::endl;
                    for(int i=0;i<PFCMU::TOTALCAMS;i++)
                    {
                        int thumWidth, thumHeight, thumIdx;
                        liveStream >> liveCmd >> thumWidth >> thumHeight >> thumIdx;
                        liveStream.get();
                        if(liveCmd=="THUM")
                        {
                            if(NULL==thumbBuf || thumbBuf->width != thumWidth || thumbBuf->height != thumHeight)
                            {
                                cvReleaseImage(&thumbBuf);
                                thumbBuf = cvCreateImage(cvSize(thumWidth,thumHeight),IPL_DEPTH_8U,1);
                            }
                            liveStream.read(thumbBuf->imageData,thumWidth*thumHeight);
                            IplImage *imgBuf = m_ThumbImg.at(thumIdx)->getOrigIplImage();
                            cvCopy(thumbBuf,imgBuf);
                        }
                    }
                    int fullWidth,fullHeight,fullIdx;
                    liveStream >> liveCmd >> fullWidth >> fullHeight >> fullIdx;
                    liveStream.get();
                    if(liveCmd=="FULL")
                    {
                        if(NULL==blackBuf || blackBuf->width != fullWidth || blackBuf->height != fullHeight)
                        {
                            cvReleaseImage(&blackBuf);
                            blackBuf = cvCreateImage(cvSize(fullWidth,fullHeight),IPL_DEPTH_8U,1);
                        }
                        liveStream.read(blackBuf->imageData,fullWidth*fullHeight);
                        IplImage *imgBuf = m_LiveImg->getOrigIplImage();
                        cvCopy(blackBuf,imgBuf);
                    }
                    emit allImageDataReceived();
//                    for(int i=0;i<m_ThumbImg.count();i++)
//                    {
//                        m_ThumbImg.at(i)->update();
//                    }
//                    m_LiveImg->update();
                }//if(m_IsWatching)
                else
                {
                    liveStream << "IDLE" << std::endl;
                }
            }//while(m_IsStreaming)
        }
    }
    catch(std::exception &e)
    {
        boost::mutex::scoped_lock lock(io_mu);//Still need some other lock. This lock only available in this class.
//        std::cerr << e.what() <<std::endl;
    }
}

void PFNode::startCapture(QStringList paraCapture)
{
    if(paraCapture.size()==2)
    {
        m_IsCapturing = true;
        QString strFrames = paraCapture.at(0);
        unsigned int frames = strFrames.toUInt();
        TRACE(1, "frames is %d\n", frames);
        QString strFName = paraCapture.at(1);
        std::string filename = strFName.toStdString();
        TRACE(1, "fname is %s\n", filename.c_str());
        try
        {
            using namespace boost::asio::ip;
            tcp::iostream captureStream(m_NodeName, CMDPORT);
            char captureStr[1024];
            snprintf(captureStr, sizeof(captureStr), "ENCP 0 %d %s", frames, filename.c_str());
            captureStream << "ENCP " << 0 <<" "<< frames <<" "<< filename << std::endl;
            std::string captureCmd;
            unsigned long long startts;
            std::string captureLine;
            std::getline(captureStream, captureLine);
            std::istringstream captureIss(captureLine);
            captureIss >> captureCmd;
            if(captureCmd=="CPSR")
            {
                captureIss >> startts;
                TRACE(1, "Fm %s: Capture should start on %llu.\n", m_NodeName.c_str(), startts);
            }
            else
            {
                m_IsCapturing = false;
            }
        }
        catch(std::exception &e)
        {
            m_IsCapturing = false;
        }
    }
}

void PFNode::sendCommand(std::string command)
{
    boost::thread cmdTrd(boost::bind(&PFNode::m_ControlConnect, this, m_NodeName,CMDPORT,command));
}

void PFNode::m_ControlConnect(std::string nodeName, std::string nodePort, std::string command)
{
    try
    {
        boost::mutex::scoped_lock lock(net_mu);
        std::string localCmd(command);
        using namespace boost::asio::ip;
        tcp::iostream cmdStream(nodeName, nodePort);
        std::string recvCmd;
        std::getline(cmdStream,recvCmd);
        if(recvCmd=="REDY")
        {
            cmdStream << localCmd;
            std::getline(cmdStream, recvCmd);
            if(recvCmd=="RCVD")
            {
                //no further command
            }
            else if(recvCmd=="WTRT")
            {
                std::getline(cmdStream, recvCmd);
                QString qCmd(recvCmd.c_str());
                emit clientEvent(qCmd);
            }
        }
        else
        {
            event_CommandNotSend(localCmd);
        }
    }
    catch(std::exception &e)
    {
        boost::mutex::scoped_lock lock(io_mu);//Still need some other lock. This lock only available in this class.
//        std::cout << e.what() << std::endl;
        qDebug() << e.what();
    }
}

std::string PFNode::getNodeName() const
{
    return m_NodeName;
}

void PFNode::setNodeName(std::string name)
{
    m_NodeName = name;
}

double PFNode::getShutter() const
{
    return m_Shutter;
}

double PFNode::getGain() const
{
    return m_Gain;
}

QList<GraphicsIplImgObject *> PFNode::getThumbImages() const
{
    return m_ThumbImg;
}

GraphicsIplImgObject* PFNode::getLiveImage() const
{
    return m_LiveImg;
}

void PFNode::setShutter(double shutter)
{
    m_Shutter = shutter;
    sendShutter();
}

void PFNode::sendShutter()
{
    char text[1024];
    snprintf(text, sizeof(text), "SHTR %f", m_Shutter);
    std::string cmd(text);
    sendCommand(cmd);
}

void PFNode::setGain(double gain)
{
    m_Gain = gain;
    sendGain();
}

void PFNode::sendGain()
{
    char text[1024];
    snprintf(text, sizeof(text), "GAIN %f", m_Gain);
    std::string cmd(text);
    sendCommand(cmd);
}

void PFNode::setToWatching(int cam)
{
    m_WatchingCam = cam;
    m_IsWatching = true;
}

void PFNode::setToNotWatching()
{
    m_IsWatching = false;
}

/*event*/
void PFNode::event_ConnectionEst(int fps)
{
    QString eventStr(this->m_NodeName.c_str());
    eventStr.append(": ");
    eventStr.append(QString("ProFUSION is running at %1 fps mode").arg(fps));
    emit clientEvent(eventStr);
}

void PFNode::event_CommandNotSend(std::string cmd)
{
    QString eventStr(this->m_NodeName.c_str());
    eventStr.append(": Command ");
    eventStr.append(QString(cmd.c_str()));
    eventStr.append(" is not sent");
    emit clientEvent(eventStr);
}
